package com.achievo.know.application.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.AuthenticationException;
import org.springframework.ldap.control.PagedResult;
import org.springframework.ldap.control.PagedResultsCookie;
import org.springframework.ldap.control.PagedResultsRequestControl;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.AttributesMapperCallbackHandler;
import org.springframework.ldap.core.CollectingNameClassPairCallbackHandler;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.achievo.know.application.CategoryApplication;
import com.achievo.know.application.SyncLogApplication;
import com.achievo.know.application.UserApplication;
import com.achievo.know.domain.Category;
import com.achievo.know.domain.Function;
import com.achievo.know.domain.FunctionRepository;
import com.achievo.know.domain.Role;
import com.achievo.know.domain.RoleRepository;
import com.achievo.know.domain.SyncLog;
import com.achievo.know.domain.User;
import com.achievo.know.domain.UserRepository;
import com.achievo.know.exception.UserNotFoundException;
import com.achievo.know.pojo.LoginStatus;
import com.achievo.know.pojo.RoleInit;

@Service(value="userApplication")
public class UserApplicationImpl implements UserApplication {
	
	protected Log log=LogFactory.getLog(UserApplicationImpl.class);
	
	@Autowired
	private LdapTemplate ldapTemplate;
	
	@Autowired
	private UserRepository userRepository;
	@Autowired
	private SyncLogApplication syncLogApplication;
	@Autowired
	private RoleRepository roleRepository;
	@Autowired
	private CategoryApplication categoryApplication;
	@Autowired
	private FunctionRepository functionRespository;
	
	
	
	public FunctionRepository getFunctionRespository() {
		return functionRespository;
	}

	public void setFunctionRespository(FunctionRepository functionRespository) {
		this.functionRespository = functionRespository;
	}

	public CategoryApplication getCategoryApplication() {
		return categoryApplication;
	}

	public void setCategoryApplication(CategoryApplication categoryApplication) {
		this.categoryApplication = categoryApplication;
	}

	public RoleRepository getRoleRepository() {
		return roleRepository;
	}

	public void setRoleRepository(RoleRepository roleRepository) {
		this.roleRepository = roleRepository;
	}

	public SyncLogApplication getSyncLogApplication() {
		return syncLogApplication;
	}

	public void setSyncLogApplication(SyncLogApplication syncLogApplication) {
		this.syncLogApplication = syncLogApplication;
	}

	@Override
	public int authenticate(String userName, String credentials) {
		//是否存在
		User user=userRepository.get(userName);
		if(user!=null){
			/*if(user.getLocked()){
				return LoginStatus.USERDISABLED;//已失效
			}*/
			//如果是访客,从本地验证密码
			if(user.isLocal()){
				if(!credentials.equals(user.getPassword())){
					return LoginStatus.WRONGPWD;
				}else{
					return LoginStatus.SUCCESS;
				}
			}
		}
		DirContext ctx = null;
		try {
			List<User> users=getUserByUid(userName);
			if(users.size()<=0){
				return LoginStatus.USERNOTFOUND;//找不到用户或用户不唯一
			}
			boolean isOk = false;
			if (loginCheack(userName, credentials)) {
				System.out.println(userName+" logined");
				isOk = true;
			}
			if(isOk){
				//如果本地没有则创建记录
				if(user==null){
					if(users.size()>0){
						user=users.get(0);
						createUser(user);
					}
				}
				return LoginStatus.SUCCESS;//验证成功
			}else{
				return LoginStatus.WRONGPWD;//验证出错
			}
		} catch (AuthenticationException e) {
			if(e.getMessage().indexOf("49")>0){
				return LoginStatus.WRONGPWD;//密码不对
			}
			return LoginStatus.AUTHERROR;//验证出错
		} catch (UserNotFoundException e) {
			return LoginStatus.USERNOTFOUND;//找不到用户或用户不唯一
		} finally {
			if(ctx!=null)
				LdapUtils.closeContext(ctx);
		}
	}
	
	@Override
	@Transactional
	public void createUser(User user) {
		/*Role emp = roleRepository.get(RoleInit.EMP);
		Set<Role> roles = new HashSet<Role>();
		roles.add(emp);
		user.setRoles(roles);*/
		userRepository.createUser(user);
		userRepository.addRole(user.getId(),RoleInit.EMP);
	}

	public User getUserByDn(String dn) {
	     return (User)ldapTemplate.lookup(dn, new UserAttributesMapper());
	}
	
	public List<User> getUserByUid(String uid) {
        // 获取DirContext对象
        DirContext ctx = ldapTemplate.getContextSource().getReadOnlyContext();
        ArrayList<User> users = new ArrayList<User>();
        UserAttributesMapper mapper=new UserAttributesMapper();
        try {
            SearchControls constraints = new SearchControls();
            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
            NamingEnumeration en = ctx.search("", "userPrincipalName=" + uid+"@genomics.cn", constraints);
            // 查询所有用户
            while (en != null && en.hasMoreElements()) {
                Object obj = en.nextElement();
                if (obj instanceof SearchResult) {
                    SearchResult si = (SearchResult) obj;
                    // 获取dn并添加到查询列表
                    User person=(User)mapper.mapFromAttributes(si.getAttributes());
                    users.add(person);
                }
            }
            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                ctx.close();
            } catch (Exception ee) {
                ee.printStackTrace();
            }
        }
        return users;
    }
	
	 private boolean loginCheack(String username, String password) {
	        // 获取DirContext对象
	        DirContext ctxs = ldapTemplate.getContextSource().getReadOnlyContext();
	        try {
	            // 组装登陆ldap需要的信息数据
	            Hashtable<Object, Object> ht = new Hashtable<Object, Object>();
	            // 获取已有的登陆信息
	            ht = (Hashtable<Object, Object>) ctxs.getEnvironment();
	            // 设置用户
	            ht.put(Context.SECURITY_PRINCIPAL, username+"@genomics.cn");
	            // 设置用户登陆密码
	            ht.put(Context.SECURITY_CREDENTIALS, password);
	            // 设置用户验证方式为simple
	            ht.put(Context.SECURITY_AUTHENTICATION, "simple");
	            // 出现异常时表示用当前登陆人登陆失败
	            DirContext c = new InitialDirContext(ht);
	            c.close();
	            return true;
	        } catch (Exception e) {
	            //e.printStackTrace();
	            System.out.println(username+" login false");
	            return false;
	        } finally {
	            try {
	                ctxs.close();
	            } catch (Exception ie) {

	            }
	        }
	    }
	
	//查询用户user dn
	public List<String> getUserDnByUid(String uid) {
        // 获取DirContext对象
        DirContext ctx = ldapTemplate.getContextSource().getReadOnlyContext();
        // 存储用户dn
        ArrayList<String> dn = new ArrayList<String>();
        try {
            SearchControls constraints = new SearchControls();
            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
            NamingEnumeration en = ctx.search("", "cn=" + uid, constraints);
            // 查询所有用户
            while (en != null && en.hasMoreElements()) {
                Object obj = en.nextElement();
                if (obj instanceof SearchResult) {
                    SearchResult si = (SearchResult) obj;
                    // 获取dn并添加到查询列表
                    dn.add(si.getNameInNamespace());
                }
            }
            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                ctx.close();
            } catch (Exception ee) {
                ee.printStackTrace();
            }
        }
        return dn;
    }
	public List<String> getOneLevelUnit() {
		 final List<String> us=new ArrayList<String>();
		 SearchControls searchControls = new SearchControls();
			searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
		 EqualsFilter filter = new EqualsFilter("objectclass", "organizationalUnit");
		 try {
			ldapTemplate.search("", filter
						.encode(), searchControls,new AttributesMapper() {
					public Object mapFromAttributes(Attributes attrs)
							throws NamingException {
						String name=(String)attrs.get("name").get();
						//if(!"SZ".equals(name))
							us.add(name);
						return null;
					}
				});
		} catch (Exception e) {
		}
		 return us;
	   }
	
	
	 public List<String> getAllUnit() {
		 final List<String> oneUs=getOneLevelUnit();
		 final List<String> us=new ArrayList<String>();
		 SearchControls searchControls = new SearchControls();
			searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
		 EqualsFilter filter = new EqualsFilter("objectclass", "organizationalUnit");
		 try {
			ldapTemplate.search("", filter
						.encode(), searchControls,new AttributesMapper() {
					public Object mapFromAttributes(Attributes attrs)
							throws NamingException {
						String name=(String)attrs.get("distinguishedName").get();
						String name1=(String)attrs.get("name").get();
						name=name.replace(",DC=genomics,DC=cn", "");
						boolean isOk=true;
						for(String sss:oneUs){
							if(name1.equalsIgnoreCase(sss)){
								isOk=false;
								break;
							}
						}
						if(isOk)
						us.add(name);
						return null;
					}
				});
		} catch (Exception e) {
		}
		 return us;
	   }
	 
	 public List<User> getPersonsByOu(String ou) {
		 final UserAttributesMapper mapper = new UserAttributesMapper();
		 EqualsFilter filter = new EqualsFilter("objectclass", "user");
			return ldapTemplate.search(ou, filter
					.encode(), new AttributesMapper() {
				public Object mapFromAttributes(Attributes attrs)
						throws NamingException {
					User u = (User) mapper.mapFromAttributes(attrs);
					return u;
				}
			});
	   }
	
	
	@Override
	public List<User> getAllPersons() {
		List<User> allUsers=new ArrayList<User>();
		List<String> list=getAllUnit();
		for(String o:list){
			List<User> us=getPersonsByOu((String)o);
			allUsers.addAll(us);
		}
		return allUsers;
	}
	
	public PagedResult getPagePersons(int pageSize,PagedResultsCookie cookie) {
		final UserAttributesMapper mapper = new UserAttributesMapper();
		EqualsFilter filter = new EqualsFilter("objectclass", "user");
		SearchControls searchControls = new SearchControls();
		searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
		PagedResultsRequestControl requestControl = new PagedResultsRequestControl(
				pageSize,cookie);
		CollectingNameClassPairCallbackHandler handler = new AttributesMapperCallbackHandler(
				new AttributesMapper() {
					public Object mapFromAttributes(Attributes attrs)
							throws NamingException {
						User person = (User) mapper.mapFromAttributes(attrs);
						return person;
					}
				});
		ldapTemplate.search("", filter.encode(),
				searchControls, handler, requestControl);
		return new PagedResult(handler.getList(),requestControl.getCookie());
	}
	
	public String convert(Object o){
		if(o==null)
			return null;
		return o.toString();
	}
	
	@Override
	public void dataSyncDaily() {
		log.info("====>start dataSyncDaily");
		Role commonEmp=roleRepository.get(Role.COMMONEMP);
		if(commonEmp==null){
			log.info("没找到角色信息");
			return;
		}
		List<String> userLocal=userRepository.findAllUsername();
		log.info("userLocal size:"+userLocal.size());
		List<User> updateList=new ArrayList<User>();
		List<SyncLog> logs=new ArrayList<SyncLog>();
		List<String> users=new ArrayList<String>();
		List<String> unitlist=getAllUnit();
		log.info("unitlist size:"+unitlist.size());
		if(unitlist.size()<=0){
			log.info("没找到部门信息");
			return;
		}
		for(String o:unitlist){
			if(log.isDebugEnabled())
				log.debug("start sync "+o);
			//本地没有，LDAP上有的。
			List<User> usersOfUnit=getPersonsByOu(o);
			if(log.isDebugEnabled())
				log.debug("usersOfUnit size:"+usersOfUnit.size());
			for(User u:usersOfUnit){
				if(log.isDebugEnabled())
					log.debug("---->start sync "+u.getUsername());
				users.add(u.getUsername());
				User temp=findLocal(u);
				if(temp==null){//数据库中没有则添加
					log.info("not in local db=>add");
					u.setLocal(false);
					u.setLocked(false);
					u.setRegistryDate(new Date());
					updateList.add(u);
					logs.add(SyncLog.createLog(u,SyncLog.ADD,u.toString()));
					if(updateList.size()>=50){
						try{
							bulkSave(updateList,commonEmp);
							for(SyncLog logt:logs){
								try {
									syncLogApplication.save(logt);
								} catch (Exception e) {
									log.error(e);
								}
							}
						}catch(Exception e){
							e.printStackTrace();
							log.error(e);
							logs.add(SyncLog.createLog(null,SyncLog.ERROR,e.getMessage()));
						}finally{
							updateList.clear();
							
							logs.clear();
						}
					}
				}else{//有，更新部门，EMAIL 
					if(temp.getLocked()){
						continue;
					}
					if(StringUtils.isEmpty(u.getEmail())){
							log.info("in local db=>email empty=>locked");
							temp.setLocked(true);
							userRepository.save(temp);
					}else{
						if(StringUtils.equalsIgnoreCase(temp.getEmail(), u.getEmail()) && StringUtils.equalsIgnoreCase(temp.getDeptment(), u.getDeptment()) && StringUtils.equalsIgnoreCase(temp.getRealName(), u.getRealName())){
							/*if(temp.getLocked()){
								log.info("in local db=>locked=>unlocked");
								temp.setLocked(false);
								userRepository.save(temp);
							}*/
						}else{
							log.info("in local db=>attr changed=>update");
							logs.add(SyncLog.createLog(u,SyncLog.EDIT,temp.getEmail()+","+temp.getDeptment()+"-->"+u.getEmail()+","+u.getDeptment()));
							temp.setEmail(u.getEmail());
							temp.setLocked(false);
							temp.setRealName(u.getRealName());
							temp.setDeptment(u.getDeptment());
							userRepository.save(temp);
						}
					}
				}
			}
		}
		if(updateList.size()>0){
			try{
				bulkSave(updateList,commonEmp);
				for(SyncLog logt:logs){
					try {
						syncLogApplication.save(logt);
					} catch (Exception e) {
						log.error(e);
					}
				}
			}catch(Exception e){
				e.printStackTrace();
				log.error(e);
				logs.add(SyncLog.createLog(null,SyncLog.ERROR,e.getMessage()));
			}finally{
				updateList.clear();
				
				logs.clear();
			}
		}
		//本地有，LDAP上没有的
		updateList.clear();
		for(String username:userLocal){
			if(!isInLdap(username,users)){
				log.info(username+" not in ldap=>locked");
				User u=get(username);
				updateList.add(u);
				logs.add(SyncLog.createLog(u,SyncLog.DELETE,u.toString()));
			}
		}
		if(updateList.size()>0){
			bulkDisabled(updateList);
		}
		for(SyncLog logt:logs){
			try {
				syncLogApplication.save(logt);
			} catch (Exception e) {
				log.error(e);
			}
		}
	}
	
	@Override
	public void dataSyncDaily1() {
		log.info("====>start dataSyncDaily");
		Role commonEmp=roleRepository.get(Role.COMMONEMP);
		if(commonEmp==null){
			log.info("没找到角色信息");
			return;
		}
		List<String> userLocal=userRepository.findAllUsername();
		log.info("userLocal size:"+userLocal.size());
		List<SyncLog> logs=new ArrayList<SyncLog>();
		List<String> unitlist=getAllUnit();
		log.info("unitlist size:"+unitlist.size());
		if(unitlist.size()<=0){
			log.info("没找到部门信息");
			return;
		}
		for(String username:userLocal){
			List<User> tusers=getUserByUid(username);
			if(tusers.size()<=0){//not exists
				log.info(username+" not in ldap=>locked");
				User u=get(username);
				u.setLocked(true);
				userRepository.save(u);
				logs.add(SyncLog.createLog(u,SyncLog.DELETE,u.toString()));
			}else{//exists
				User temp=get(username);
				User u=tusers.get(0);
				if(temp.getLocked()){
					continue;
				}
				if(StringUtils.isEmpty(u.getEmail())){
						log.info("in local db=>email empty=>locked");
						temp.setLocked(true);
						userRepository.save(temp);
				}else{
					if(StringUtils.equalsIgnoreCase(temp.getEmail(), u.getEmail()) && StringUtils.equalsIgnoreCase(temp.getDeptment(), u.getDeptment()) && StringUtils.equalsIgnoreCase(temp.getRealName(), u.getRealName())){
					}else{
						log.info("in local db=>attr changed=>update");
						logs.add(SyncLog.createLog(u,SyncLog.EDIT,temp.getEmail()+","+temp.getDeptment()+"-->"+u.getEmail()+","+u.getDeptment()));
						temp.setEmail(u.getEmail());
						temp.setLocked(false);
						temp.setRealName(u.getRealName());
						temp.setDeptment(u.getDeptment());
						userRepository.save(temp);
					}
				}
			}
		}
		for(SyncLog logt:logs){
			try {
				syncLogApplication.save(logt);
			} catch (Exception e) {
				log.error(e);
			}
		}
		logs.clear();
		//add
		List<String> list=getAllUnit();
		for(String unit:list){
			List<User> users=getPersonsByOu(unit);
			for(User u:users){
				System.out.println(u.getRealName());
				User temp=get(u.getUsername());
				if(temp==null){
					System.out.println("addddd:"+u.getRealName());
					createUser(u);
					logs.add(SyncLog.createLog(u,SyncLog.ADD,u.toString()));
				}
			}
		}
		for(SyncLog logt:logs){
			try {
				syncLogApplication.save(logt);
			} catch (Exception e) {
				log.error(e);
			}
		}
	}

	private boolean isInLdap(String u, List<String> users) {
		for(String ldapUser:users){
			if(u.equals(ldapUser)){
				return true;
			}
		}
		return false;
	}
	
	private User findLocal(User u) {
		User temp=this.get(u.getUsername());
		return temp;
	}

	@Override
	public void dataSyncFirst() {
		Role commonEmp=roleRepository.get(Role.COMMONEMP);
		if(commonEmp==null){
			log.info("没找到角色信息");
			return;
		}
		List<User> users=getAllPersons();
		List<User> updateList=new ArrayList<User>();
		List<SyncLog> logs=new ArrayList<SyncLog>();
		for(User u:users){
			u.setLocal(false);
			u.setLocked(false);
			u.setRegistryDate(new Date());
			updateList.add(u);
			logs.add(SyncLog.createLog(u,SyncLog.ADD,u.toString()));
			if(updateList.size()>=50){
				try{
					bulkSave(updateList,commonEmp);
					for(SyncLog log:logs){
						syncLogApplication.save(log);
					}
				}catch(Exception e){
					e.printStackTrace();
					log.error(e);
					logs.add(SyncLog.createLog(null,SyncLog.ERROR,e.getMessage()));
				}finally{
					updateList.clear();
					logs.clear();
				}
			}
		}
		if(updateList.size()>0){
			try{
				bulkSave(updateList,commonEmp);
				for(SyncLog log:logs){
					syncLogApplication.save(log);
				}
			}catch(Exception e){
				e.printStackTrace();
				log.error(e);
				logs.add(SyncLog.createLog(null,SyncLog.ERROR,e.getMessage()));
			}finally{
				updateList.clear();
				
				logs.clear();
			}
		}
	}
	
	
	
	@Override
	public User get(String username) {
		return userRepository.get(username);
	}

	@Override
	public Map<String, String> findUserAuth(User loginUser) {
		Map<String, String> auths=new HashMap<String,String>();
		if(loginUser.getRolesStr().contains("超级管理员")){
			List<Function> fs=functionRespository.findAll();
			for(Function f:fs){
				auths.put(f.getName(), f.getAlias());
			}
			return auths;
		}
		Iterator<Role> i= loginUser.getRoles().iterator();
		while(i.hasNext()){
			Role r=i.next();
			Iterator<Function> fs=r.getFunctions().iterator();
			while(fs.hasNext()){
				Function f=fs.next();
				auths.put(f.getName(), f.getAlias());
			}
		}
		return auths;
	}
	
	public void addAll(List<Long> cateIds,Category c){
		if(!cateIds.contains(c.getId())){
			cateIds.add(c.getId());
			if(c.getParent()!=null){
				addAll(cateIds,c.getParent());
			}
		}
		/*List<Category> children=c.getChildren();
		if(children.size()>0){
			for(Category cc:children){
				addAll(cateIds,cc);
			}
		}*/
	}
	@Override
	public List<Long> findUserCates(User loginUser) {
		List<Long> cateIds=new ArrayList<Long>();
		Set<Category> categorys=categoryApplication.getUserCates(loginUser);//loginUser.getCategorys();
		if(categorys.size()>0){
			Iterator<Category> i= categorys.iterator();
			while(i.hasNext()){
				Category c=i.next();
				addAll(cateIds,c);
			}
			return cateIds;
		}else{
			Iterator<Role> i= loginUser.getRoles().iterator();
			while(i.hasNext()){
				Role r=i.next();
				if(r.isAdmin()){
					continue;
				}
				Iterator<Category> ii= r.getCategorys().iterator();
				while(ii.hasNext()){
					Category cc=ii.next();
					addAll(cateIds,cc);
				}
			}
		}
		return cateIds;
	}
	

	@Override
	public List<Long> findAdminCates(User loginUser) {
		List<Long> cateIds=new ArrayList<Long>();
		Iterator<Role> i= loginUser.getRoles().iterator();
		while(i.hasNext()){
			Role r=i.next();
			if(r.isAdmin()){
				Iterator<Category> ii= r.getCategorys().iterator();
				while(ii.hasNext()){
					Category cc=ii.next();
					addAll(cateIds,cc);
				}
			}
		}
		return cateIds;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public void bulkSave(List<User> users, Role commonEmp) {
		userRepository.bulkSave(users,commonEmp);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public void bulkDisabled(List<User> users) {
		userRepository.bulkDisable(users);
		
	}

	public LdapTemplate getLdapTemplate() {
		return ldapTemplate;
	}

	public void setLdapTemplate(LdapTemplate ldapTemplate) {
		this.ldapTemplate = ldapTemplate;
	}

	public UserRepository getUserRepository() {
		return userRepository;
	}

	public void setUserRepository(UserRepository userRepository) {
		this.userRepository = userRepository;
	}
	
	

}
